package com.liao.quart.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.liao.quart.entity.QuartzJob;
import com.liao.quart.entity.QuartzLog;
import com.liao.quart.entity.from.QuartzJobFrom;
import com.liao.quart.entity.from.QuartzLogFrom;
import com.liao.quart.mapper.QuartzJobMapper;
import com.liao.quart.quart.QuartzManage;
import com.liao.quart.service.QuartzJobService;
import com.liao.quart.utils.DateUtils;
import com.liao.quart.utils.RedisUtil;
import com.liao.quart.utils.SpringContextHolder;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;

/**
 * @author JP0020
 * @description 针对表【sys_quartz_job(定时任务)】的数据库操作Service实现
 * @createDate 2023-06-01 11:37:32
 */
@Service
public class QuartzJobServiceImpl extends ServiceImpl<QuartzJobMapper, QuartzJob>
        implements QuartzJobService {
    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private QuartzManage quartzManage;

    @Override
    public void add(QuartzJobFrom from) throws Exception {
        from.setCreateBy("测试员");
        if (from.getId() != null) {
            throw new Exception("ID不用传参");
        }
        // 验证Bean是不是合法的，合法的定时任务 Bean 需要用 @Service 定义
        checkBean(from.getBeanName());
        QuartzJob quartzJob = BeanUtil.copyProperties(from, QuartzJob.class);
        this.getBaseMapper().insert(quartzJob);
        quartzManage.addJob(quartzJob);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateJob(QuartzJobFrom from) throws Exception {
        checkBean(from.getBeanName());
        if (!CronExpression.isValidExpression(from.getCronExpression())) {
            throw new Exception("cron编码错误");
        }
        if (StringUtils.isNotBlank(from.getSubTask())) {
            List<String> tasks = Arrays.asList(from.getSubTask().split("[,，]"));
            if (tasks.contains(from.getId().toString())) {
                throw new Exception("子任务中不能添加当前任务ID");
            }
        }
        this.getBaseMapper().updateById(BeanUtil.copyProperties(from, QuartzJob.class));
        quartzManage.updateJobCron(BeanUtil.copyProperties(from, QuartzJob.class));
    }

    @Override
    public List<QuartzJob> queryList(QuartzJobFrom from) throws Exception {
        Page<QuartzJob> page = PageHelper.startPage(from.getPageNum(), from.getPageSize());
        if (from.getCreateDateRange() != null) {
            String[] createDateRange = from.getCreateDateRange();
            from.setCreateDateStart(DateUtils.getDateFormat().parse(createDateRange[0]));
            from.setCreateDateEnd(DateUtils.getDateFormat().parse(createDateRange[1]));
        }
        this.getBaseMapper().selectDataList(from);
        PageInfo<QuartzJob> pageInfo = page.toPageInfo();
        return pageInfo.getList();
    }

    @Override
    public QuartzJob getQuartzJobById(QuartzJobFrom from) {
        return this.getBaseMapper().selectOne(new QueryWrapper<QuartzJob>().eq("id", from.getId()).eq("is_del", 1));
    }

    @Override
    public void execution(QuartzJob quartzJob) throws Exception {
        quartzManage.runJobNow(quartzJob);
    }

    @Override
    public void updateIsEnable(QuartzJob quartzJob) throws Exception {
        // 0不启用   1启用
        if (quartzJob.getIsEnable().equals("0")) {
            quartzManage.resumeJob(quartzJob);
            quartzJob.setIsEnable("1");
        } else {
            quartzManage.pauseJob(quartzJob);
            quartzJob.setIsEnable("0");
        }
        this.getBaseMapper().updateById(quartzJob);
    }

    @Override
    public void delete(QuartzJobFrom from) throws Exception {
        for (Long id : from.getIds()) {
            QuartzJob quartzJob = getQuartzJobById(new QuartzJobFrom().setId(id));
            quartzManage.deleteJob(quartzJob);
            this.getBaseMapper().update(null, new UpdateWrapper<QuartzJob>()
                    .set("is_del", 0)
                    .eq("id", id)
                    .eq("update_by", from.getUpdateBy())
                    .eq("update_date", new DateTime())
            );
        }
    }

    @Override
    public List<QuartzLog> queryAllLog(QuartzLogFrom from) throws Exception {
        Page<QuartzLog> page = PageHelper.startPage(from.getPageNum(), from.getPageSize());
        if (from.getCreateDateRange() != null) {
            String[] createDateRange = from.getCreateDateRange();
            from.setCreateDateStart(DateUtils.getDateFormat().parse(createDateRange[0]));
            from.setCreateDateEnd(DateUtils.getDateFormat().parse(createDateRange[1]));
        }
        this.getBaseMapper().queryAllLog(from);
        PageInfo<QuartzLog> pageInfo = page.toPageInfo();
        return pageInfo.getList();
    }

    @Async
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void executionSubJob(String[] tasks) throws Exception {
        for (String id : tasks) {
            if (StrUtil.isBlank(id)) {
                // 如果是手动清除子任务id，会出现id为空字符串的问题
                continue;
            }
            QuartzJob quartzJob = this.getBaseMapper().selectById(Long.parseLong(id));
            // 执行任务
            String uuid = IdUtil.simpleUUID();
            quartzJob.setUuid(uuid);
            // 执行任务
            execution(quartzJob);
            // 获取执行状态，如果执行失败则停止后面的子任务执行
            Boolean result = (Boolean) redisUtil.get(uuid);
            while (result == null) {
                // 休眠5秒，再次获取子任务执行情况
                Thread.sleep(5000);
                result = (Boolean) redisUtil.get(uuid);
            }
            if (!result) {
                redisUtil.del(uuid);
                break;
            }
        }
    }

    private void checkBean(String beanName) throws Exception {
        // 避免调用攻击者可以从SpringContextHolder获得控制jdbcTemplate类
        // 并使用getDeclaredMethod调用jdbcTemplate的queryForMap函数，执行任意sql命令。
        List<String> allServiceBeanName = SpringContextHolder.getAllServiceBeanName();
        if (!SpringContextHolder.getAllServiceBeanName().contains(beanName)) {
            throw new Exception("非法bean！");
        }
    }
}